package br.edu.unifei.cct720.trabalho04.utils.formas;

import java.awt.Color;

import br.edu.unifei.cct720.trabalho04.utils.ponto.Ponto;
import br.edu.unifei.cct720.trabalho04.utils.ponto.Pontos;

/**
 * Classe responsavel por representar a forma
 * geometrica de uma reta
 * 
 * @author fknappe
 *
 */
public class Reta implements FormaGeometrica {

	// Declaracao das variaveis de instacia
	private Ponto pInicial = null;
	private Ponto pFinal = null;
	private Color cor = Color.BLACK;
	
	/**
	 * Construtor
	 * 
	 * @param Ponto inicio
	 * @param Ponto fim
	 */
	public Reta(Ponto inicio, Ponto fim) {
		super();
		pInicial = inicio;
		pFinal = fim;
	}
	
	/**
	 * Metodo responsavel pelo calculo do coeficiente angular da reta
	 * 
	 * @return float
	 */
	private float calculaM () {
		return  (float)( this.getPFinal().getY() - this.getPInicial().getY() ) /
		( this.getPFinal().getX() - this.getPInicial().getX() );		
	}
	
	/**
	 * Metodo responsavel pelo calculo do coeficiente livre da reta
	 * 
	 * @return floar
	 */
	private float calculaB () {
		return this.getPInicial().getY() - calculaM () * this.getPInicial().getX();
	}
	
	/**
	 * Metodo responsavel por retornar todos os pontos
	 * pertencentes a reta
	 * 
	 * @return Pontos
	 */
	public Pontos getPontos() {	
		if (calculaM() == 0) return horizontal();
		return ddaInteiro();
	}

	/**
	 * Metodo responsavel por construir uma reta horizontal
	 * 
	 * @return Pontos
	 */
	private Pontos horizontal() {
		Pontos pontos = new Pontos();
		for (int x = this.getPInicial().getX(); x <= this.getPFinal().getX(); x++)
			pontos.add(new Ponto(x, this.getPInicial().getY()));	
		return pontos; 
	}
	
	/**
	 * Metodo responsavel por construir uma reta vertical
	 * 
	 * @return Pontos
	 */
	private Pontos vertical() {
		Pontos pontos = new Pontos();
		for (int y = this.getPInicial().getY(); y <= this.getPFinal().getY(); y++)
			pontos.add(new Ponto(this.getPInicial().getX(), y));		
		return pontos;	
	}
	
   /**
    * Metodo responsavel por construir uma reta utilizando o metodo 
    * DDA inteiro
    * 
    * @return Pontos
    */
	private Pontos ddaInteiro() {
		Pontos pontos = new Pontos();
	   
	    int deltaX = this.getPFinal().getX() - this.getPInicial().getX();
		int deltaY = this.getPFinal().getY() - this.getPInicial().getY();
		int erro = 0;
		int x = this.getPInicial().getX();
		int y = this.getPInicial().getY();
		
		// Inverte pontos
		if ((Math.abs(deltaY) >= Math.abs(deltaX) && deltaY<0) 
				|| (Math.abs(deltaY) < Math.abs(deltaX) && (deltaY < 0))) {
			x = this.getPFinal().getX();
			y = this.getPFinal().getY();
			deltaX = this.getPInicial().getX() - this.getPFinal().getX();
			deltaY = this.getPInicial().getY() - this.getPFinal().getY();
		}
		
		this.getPInicial().setCor(this.cor);
		pontos.add(this.getPInicial());
		
		if (deltaX >= 0) {
			if (Math.abs(deltaX) >= Math.abs(deltaY)) { 
				// Caso 1
				for (int i = 1; i < Math.abs(deltaX); i++) {
					if (erro < 0) {
						x++;
						pontos.add(new Ponto(x, y));
						erro += deltaY;
					} else {
						x++;
						y++;
						pontos.add(new Ponto(x, y));
						erro += deltaY - deltaX;
					}
				}
			} else {
				// Caso 2
				for (int i = 1; i < Math.abs(deltaY); i++) {
					if (erro < 0) {
						x++;
						y++;
						pontos.add(new Ponto(x, y));
						erro += deltaY - deltaX;
					} else {
						y++;
						pontos.add(new Ponto(x, y));
						erro -= deltaX;
					}
				}
			}
		} else {
			if (Math.abs(deltaX) >= Math.abs(deltaY)) {
				// Caso 4
				for (int i = 1; i < Math.abs(deltaX); i++) {
					if (erro < 0) {
						x--;
						pontos.add(new Ponto(x, y));
						erro += deltaY;
					} else {
						x--;
						y++;
						pontos.add(new Ponto(x, y));
						erro += deltaY + deltaX;
					}
				}
			} else {
				// Caso 3
				for (int i = 1; i < Math.abs(deltaY); i++) {
					if (erro < 0) {
						x--;
						y++;
						pontos.add(new Ponto(x, y));
						erro += deltaX + deltaY;
					} else {
						y++;
						pontos.add(new Ponto(x, y));
						erro += deltaX;
					}
				}
			}
		}	
		this.getPFinal().setCor(this.cor);
		pontos.add(this.getPFinal());
		return pontos;
	}
		
   //Metodos getters e setters
   public Color getCor() {
		return this.cor;
	}

	public void setCor(Color cor) {
		this.cor = cor;
	}

	public Ponto getPFinal() {
		return pFinal;
	}
	
	public void setPFinal(Ponto fim) {
		pFinal = fim;
	}
	
	public Ponto getPInicial() {
		return pInicial;
	}
	
	public void setPInicial(Ponto inicial) {
		pInicial = inicial;
	}	
}
